#include "StdAfx.h"
#include "Renderer.h"
#include "CG_skel_w_MFC.h"
#include "InitShader.h"
#include "GL\freeglut.h"
#include "PrimMeshModel.h"

#define INDEX(width,x,y,c) (x+y*width)*3+c
#define Z_INDEX(width,x,y) (x+y*width)

template <typename T>
T interpolate(const T& v1, const T& v2, GLfloat ratio) {
	return (v1*ratio) + (v2*(1-ratio));
}

template <typename T>
T interpolate(const T& v1, const T& v2, const T& v3, GLfloat ratio1, GLfloat ratio2) {
	return v1*ratio1 + v2*ratio2+v3*(1-ratio1-ratio2);
}

Renderer::Renderer() :	m_width(512), m_height(512),flagDrawNormals(false),flagDrawVertexNormals(false),flagDrawBoundingBox(false),
                        m_regularPen(1,1,0), m_activePen(0,1,1),m_currentPen(&m_activePen), m_shadingMode(SHAD_NONE),m_currentMaterial(NULL)
{
	// heinrich: remove it later!
	LightSource * l = new PointLightSource(vec3(0.3,0,0),vec3(0,1,0),vec3(0,1,0),vec4(0,0,3,1));
	m_lights.push_back(l);
	m_rightBound = m_leftBound = NULL;
	InitOpenGLRendering();
	CreateBuffers(512,512);
}
Renderer::Renderer(int width, int height) :	m_width(width), m_height(m_height),flagDrawNormals(false),flagDrawVertexNormals(false),flagDrawBoundingBox(false),
                                            m_regularPen(1,1,0), m_activePen(0,1,1),m_currentPen(&m_activePen), m_shadingMode(SHAD_NONE),
											m_currentMaterial(NULL)
{
	// heinrich: remove it later!
	LightSource * l = new PointLightSource(vec3(0.3,0,0),vec3(0,1,0),vec3(0,1,0),vec4(0,3,3,1));
	m_lights.push_back(l);
	m_rightBound = m_leftBound = NULL;
	InitOpenGLRendering();
	CreateBuffers(width,height);
}

Renderer::~Renderer(void)
{
	m_rightBound = m_leftBound = NULL;
	delete this->m_outBuffer; // Heinrich...
	delete this->m_zbuffer;
}

void Renderer::ReCreateBuffers(int width, int height) {
	delete m_outBuffer;
	delete m_zbuffer;
	CreateBuffers(width,height);
}


void Renderer::CreateBuffers(int width, int height)
{
	m_width=width;
	m_height=height;	
	CreateOpenGLBuffer(); //Do not remove this line.
	m_outBuffer = new float[3*m_width*m_height];
	int size  = m_width*m_height;
	m_zbuffer = new FastArray<float>(20,width*height);
}

void Renderer::SetDemoBuffer()
{
	this->DrawLine(vec2(10,10),vec2(10,100));
}

vec2 Renderer::TransformVertex(const vec4 & v){
	vec4 u = this->m_objectMatrix*v;
	return ProjectVertex(u);
}

vec2 Renderer::ProjectVertex(const vec4 & v) {
	vec4 u = this->m_projectionMatrix*v;
	u.perspectiveDivide();
	u = mat4(	vec4(1,0,0,0),
				vec4(0,1,0,0),
				vec4(0,0,0,0),
				vec4(0,0,0,1))*u;
	vec2 w = vec2(u.x,u.y);
	this->TransformToViewport(w);
	return w;	
}

/*
transforms a vector to normalized clipping space..., with perspective division...
*/

vec4 Renderer::applyProjectionTransform(const vec4& v) const {
	vec4 u = m_projectionMatrix * v;
	u.perspectiveDivide();
	return u;
}

vec4 Renderer::Transform2NSC(const vec4 & v) {
	vec4 u = m_projectionMatrix * m_objectMatrix * v;
	u.perspectiveDivide();
	return u;
}

vec2 Renderer::Transform2Screen(const vec4 & v) {
	vec2 w = vec2(v.x,v.y);
	this->TransformToViewport(w);
	return w;	
}

static bool isInsideNCS(const vec4 & v){
	if ((v.x<-1) || (v.x>1) || (v.y < -1) || (v.y > 1) || (v.z<-1) || (v.z > 1)) return false;
	return true;
}

// this method is about to be removed...
void Renderer::DrawSingleTriangle(const vec4 & v1, const vec4 & v2, const vec4 & v3) {
	vec4 u1, u2, u3;
	vec2 w1, w2, w3;
	u1 = Transform2NSC(v1);
	u2 = Transform2NSC(v2);
	u3 = Transform2NSC(v3);
	w1 = Transform2Screen(u1);
	w2 = Transform2Screen(u2);
	w3 = Transform2Screen(u3);
	if (isInsideNCS(u1)) {
		if (isInsideNCS(u2)) {
			DrawLine(w1,w2);
		}
		if (isInsideNCS(u3)) {
			DrawLine(w1,w3);
		}
	}
	if (isInsideNCS(u2) && isInsideNCS(u3)) {
		DrawLine(w2,w3);
	}
	drawNormalForTriangle(v1,v2,v3,m_normalLength);
}

template <class T>
static void swapElems(T * arr,int i, int j) {
	T temp = arr[i];
	arr[i]=arr[j];
	arr[j]= temp;
}

static void findMinVertex(vec2 arrViewport[3], vec4 arrSpace[3], vec4 arrNsc[3], vec4 arrNorm[3]){
	for (int i=0; i<2; i++) {
		for (int j=i+1; j<3; j++) {
			if (arrViewport[i].x > arrViewport[j].x) {
				swapElems(arrViewport,i,j);
				swapElems(arrSpace,j,j);
				swapElems(arrNsc,i,j);
				swapElems(arrNorm,i,j);
			}
		}
	}
}

void Renderer::calculateColorForVertex(	const vec4& vertexInCamFrame, const vec3& normalInCamFrame,
										vec3 & totalAmbient, vec3 & totalDiffuse, vec3 & totalSpecular) {

	vec3 reflectionDirection;
	vec3 viewersEyeDirection = normalize(vec4tovec3(-vertexInCamFrame));
	vec3 ambientAcc(0,0,0);
	vec3 diffuseAcc(0,0,0);
	vec3 specularAcc(0,0,0);

	for (vector<LightSource*>::const_iterator it = m_lights.begin(); it!=m_lights.end(); it++) {
		vec3 lightHitDirection = safeNormalize((*it)->getHitDirection(vertexInCamFrame));
		ambientAcc += (*it)->getAmbient();

		// now as we know the hit direction we can calculate the color... right?
		GLfloat dotProd = dot(safeNormalize(normalInCamFrame),lightHitDirection);
		if (dotProd<0) dotProd = 0;
		ASSERT(dotProd<=1);
		diffuseAcc = diffuseAcc + (*it)->getDiffuse()*m_currentMaterial->getDiffuse()*dotProd;
		reflectionDirection = normalize(2*dot(lightHitDirection,normalInCamFrame)*normalInCamFrame-lightHitDirection);

		// now do the same trick for the specular light...
		dotProd = dot(reflectionDirection,viewersEyeDirection);
		ASSERT(dotProd<=1);
		if (dotProd<0) dotProd = 0;
		vec3 specTerm = (*it)->getSpecular()*m_currentMaterial->getSpecular()*pow(dotProd,m_currentMaterial->getSpecularity());
		specularAcc = specularAcc + specTerm;
	}	
	totalAmbient = ambientAcc;
	totalDiffuse = diffuseAcc;
	totalSpecular = specularAcc;
}

void Renderer::DrawSingleTriangle(	const vec4& v1,  vec4 n1,
									const vec4& v2,  vec4 n2,
									const vec4& v3,  vec4 n3){
	
	vec4 u1, u2, u3;
	vec4 t1,t2,t3;
	vec2 w1, w2, w3;

	ASSERT(n1.w == 0); ASSERT(n2.w == 0); ASSERT(n3.w == 0);

	//Heinrich: Hardcoding for the sake of testing only...

	m_shadingMode = SHAD_GUE;
	m_currentMaterial = new Material(vec3(0.3,0,0),vec3(0,0,0),vec3(0,1,0),4);

	u1 = applyModelViewTransform(v1);
	u2 = applyModelViewTransform(v2);
	u3 = applyModelViewTransform(v3);
	//there is a bug here!
	vec4 m1 = applyModelViewTransform(n1);
	vec4 m2 = applyModelViewTransform(n2);
	vec4 m3 = applyModelViewTransform(n3);
	//hardcoding ends here...
	t1 = applyProjectionTransform(u1);
	t2 = applyProjectionTransform(u2);
	t3 = applyProjectionTransform(u3);
	w1 = round(Transform2Screen(t1));
	w2 = round(Transform2Screen(t2));
	w3 = round(Transform2Screen(t3));

	m_scanStart = min(min(w1.y,w2.y),w3.y);
	m_scanEnd = max(max(w1.y,w2.y),w3.y);
	if (m_leftBound!=NULL) delete m_leftBound;
	if (m_rightBound!=NULL) delete m_rightBound;
	m_leftBound		= new FastArray<int>(-1,m_scanEnd - m_scanStart+1);
	m_rightBound	= new FastArray<int>(-1,m_scanEnd - m_scanStart+1);
	m_linesDrawn = 0;
	vec2 arrViewport[3] = {w1,w2,w3};
	vec4 arrSpace[3] = {u1,u2,u3};
	vec4 arrNSC[3] = {t1,t2,t3};
	vec4 arrNorm[3] = {m1,m2,m3};
	findMinVertex(arrViewport,arrSpace,arrNSC,arrNorm);
	
	w1 = arrViewport[0]; w2 = arrViewport[1]; w3 = arrViewport[2];
	u1 = arrSpace[0]; u2 = arrSpace[1]; u3 = arrSpace[2];
	t1 = arrNSC[0]; t2 = arrNSC[1]; t3 = arrNSC[2];
	m1 = arrNorm[0]; m2 = arrNorm[1]; m3 = arrNorm[2];

	VertexData vd1 = VertexData(u1,w1,t1.z,m1);
	VertexData vd2 = VertexData(u2,w2,t2.z,m2);
	VertexData vd3 = VertexData(u3,w3,t3.z,m3);

	vec3 normal;

	switch (m_shadingMode) { // what the fuck?!
	case SHAD_NONE:
		// draw in wireframe...
		DrawSingleTriangle(v1,v2,v3);
		break;
	case SHAD_FLAT:
		{
		// now we have to draw each triangle as a filled polygon,
		// using the assigned material of the polygon...

		ASSERT(m_currentMaterial!=NULL);

		normal = normalize(calculateNormal(u1,u2,u3));

		if (normal.z < 0) return; // do not render polygons pointing outward...

		vec4 center = (1.0/3.0)*(u1+u2+u3);
		vec3 totalAmbient, totalDiffuse, totalSpecular;
		this->calculateColorForVertex(center,normal,totalAmbient,totalDiffuse,totalSpecular);
		this->m_flatPolygonColor = totalAmbient + totalDiffuse + totalSpecular;
		}

		break;
	case SHAD_GUE:
		ASSERT(m_currentMaterial!=NULL);
		normal = normalize(calculateNormal(u1,u2,u3));

		if (normal.z<0) return;

		calculateColorForVertex(vd1.m_3dCamFrameCoords, vec4tovec3(vd1.m_normalInCamFrame),
			vd1.m_ambientColor,vd1.m_diffuseColor,vd1.m_specularColor);
		
		calculateColorForVertex(vd2.m_3dCamFrameCoords, vec4tovec3(vd2.m_normalInCamFrame),
			vd2.m_ambientColor,vd2.m_diffuseColor,vd2.m_specularColor);
		
		calculateColorForVertex(vd3.m_3dCamFrameCoords, vec4tovec3(vd3.m_normalInCamFrame),
			vd3.m_ambientColor,vd3.m_diffuseColor,vd3.m_specularColor);

	}

	//decide correctly on the order of printing the vertices...

	GLfloat slope2w2 = (w2.y-w1.y)/(w2.x-w1.x);
	GLfloat slope2w3 = (w3.y - w1.y)/(w3.x-w1.x);

	if ((slope2w2 * slope2w3)<=0) {	
		if (isInsideNCS(t1) && isInsideNCS(t2)) DrawLine(vd1,vd2);
		if (isInsideNCS(t1) && isInsideNCS(t3)) DrawLine(vd1,vd3);
		if (isInsideNCS(t2) && isInsideNCS(t3)) DrawLine(vd2,vd3);
	} else {
		slope2w2 = abs(slope2w2);
		slope2w3 = abs(slope2w3);
		if (slope2w2>slope2w3) { //first draw the line with the steepest slope
			if (isInsideNCS(t1) && isInsideNCS(t2)) DrawLine(vd1,vd2);
			if (isInsideNCS(t2) && isInsideNCS(t3)) DrawLine(vd2,vd3);
			if (isInsideNCS(t1) && isInsideNCS(t3)) DrawLine(vd1,vd3);
				
		} else {
			if (isInsideNCS(t1) && isInsideNCS(t3)) DrawLine(vd1,vd3);
			if (isInsideNCS(t2) && isInsideNCS(t3)) DrawLine(vd2,vd3);
			if (isInsideNCS(t1) && isInsideNCS(t2)) DrawLine(vd1,vd2);
		}
	}
	if (m_linesDrawn==3) scanAndFill(vd1,vd2,vd3);
	cout << "\n\n\n";
}

void Renderer::DrawLine(const VertexData& vd1,
						const VertexData& vd2) {

	if (vd1.m_2dCoords.x>vd2.m_2dCoords.x) {
		this->DrawLine(vd2,vd1);
		return;
	}

	int x = vd1.m_2dCoords.x;
	int y = vd1.m_2dCoords.y;
	int yStart = vd1.m_2dCoords.y;
	int yGoal = vd2.m_2dCoords.y;
	int xGoal = vd2.m_2dCoords.x;
	int dx = xGoal - x;
	int dy = yGoal - y;
	int yStep, xStep=1;
	int primaryStep, secondaryStep;
	int * primaryVar;
	int * secondaryVar;
	if (dy<0) {
		dy*=-1;
		yStep = -1;
	}
	else {
		yStep = 1;
	}
	if (dy>dx) {
		SWAP(dy,dx);
		primaryVar = &y;
		primaryStep = yStep;
		secondaryVar = &x;
		secondaryStep = xStep;
	}
	else {
		primaryVar = &x;
		primaryStep = xStep;
		secondaryVar = &y;
		secondaryStep = yStep;
	}
	// steps variable is always counting the number of steps in the primary var!
	int steps = dx+1; 
	int d = 2*dy-dx;
	int de = 2*dy;
	int dne = 2*(dy-dx);
	while (steps>0) {

		// we should plot the pixel according to the shading model and to the z-values and the normals...
		// AVOID division by zero, or commit a siucide!
		
		//GLfloat ratio = ((GLfloat)(abs(y-v2.y)+1)/((GLfloat)(abs(v1.y-v2.y)+1)));
		GLfloat ratio = ((GLfloat)steps)/((GLfloat)(dx+1));

		ASSERT(ratio<=1);
		GLfloat averageZValue; // = z1*ratio + z2*(1-ratio);
		vec3 aggregateColor;
		this->calculateColorAndZVal(vd1,vd2,ratio,aggregateColor,averageZValue);
		PlotPixel(x,y,averageZValue,aggregateColor);
		//PlotPixel(x,y);
		int left = m_leftBound->getFrom(y-m_scanStart);
		int right = m_rightBound->getFrom(y-m_scanStart);
		ASSERT(y-m_scanStart>=0);
		ASSERT((y-m_scanStart)<=(m_scanEnd-m_scanStart));
		if (left==-1) {
			m_leftBound->putAt(y-m_scanStart,x);
			m_rightBound->putAt(y-m_scanStart,x);
		}
		else if (left == right) {
			if (x==right+1) {
				m_leftBound->putAt(y-m_scanStart,x);
				m_rightBound->putAt(y-m_scanStart,x);
			}
			if (x>right+1) {
				m_leftBound->putAt(y-m_scanStart,right);
				m_rightBound->putAt(y-m_scanStart,x);
			}
		} else {
		}
		if (d<0) {
			d+=de;
		}
		else {
			d+=dne;
			*secondaryVar=(*secondaryVar)+secondaryStep;
		}
		*primaryVar = (*primaryVar)+primaryStep;
		steps--;
	}
	m_linesDrawn++;
}

vec3 Renderer::calculateNormal(const vec4& v1,const vec4& v2,const vec4& v3){

	vec3 v1Real = vec3(v1.x/v1.w,v1.y/v1.w,v1.z/v1.w);
	vec3 v2Real = vec3(v2.x/v2.w,v2.y/v2.w,v2.z/v2.w);
	vec3 v3Real = vec3(v3.x/v3.w,v3.y/v3.w,v3.z/v3.w);
	
	vec3 e1Real = v2Real-v1Real;
	vec3 e2Real = v3Real-v1Real;


	vec3 normalReal = cross(e1Real,e2Real);

	return normalReal;

}

//triangle center is defined as the meeting of the tichons, because it's always inside the triangle. tichons cross eachother in a 2/3 ratio hence the calculation.
vec3 Renderer::calculateTriangleCenter(const vec4& v1,const vec4& v2,const vec4& v3){
	vec3 v1Real = vec3(v1.x/v1.w,v1.y/v1.w,v1.z/v1.w);
	vec3 v2Real = vec3(v2.x/v2.w,v2.y/v2.w,v2.z/v2.w);
	vec3 v3Real = vec3(v3.x/v3.w,v3.y/v3.w,v3.z/v3.w);

	vec3 v1v2 = v2Real-v1Real;

	vec3 tichon = v1Real+(v1v2/2)-v3Real;

	return vec3(v3Real+((tichon*2)/3));
}

void Renderer::drawNormalForTriangle(const vec4& v1,const vec4& v2,const vec4& v3, GLfloat modelDiameter){
	if(flagDrawNormals == false){
		return;
	}
	vec3 normal = normalize(calculateNormal(v1,v2,v3));
	vec3 triangleCenter = calculateTriangleCenter(v1,v2,v3);
	vec3 normalHead = triangleCenter + (modelDiameter/20.0)*(normal);

	vec2 w1 = this->TransformVertex(vec4(triangleCenter));
	vec2 w2 = this->TransformVertex(vec4(normalHead));
	this->DrawLine(w1,w2);
}
void Renderer::DrawPlusAtVertex(const vec4 & v) {
	vec4 u = Transform2NSC(v);
	if (isInsideNCS(u)) {
		vec2 w = Transform2Screen(u);
		vec2 w1 = vec2(w.x,w.y-2);
		vec2 w2 = vec2(w.x,w.y+2);
		vec2 w3 = vec2(w.x-2,w.y);
		vec2 w4 = vec2(w.x+2,w.y);
		DrawLine(w1,w2);
		DrawLine(w3,w4);
	}
}

void Renderer::DrawTriangles(const vector<vec3> * vertices, const vector<vec3> * normals) {
	// we still ignore the normals...
	unsigned int i=0;
	vector<vec3>::const_iterator it = vertices->begin();
	vector<vec3>::const_iterator it2 = normals->begin();
	//while (i<(vertices->size())) {
	while (it != vertices->end() && it2!=normals->end()){
		vec4 v1 = vec4(it->x, it->y, it->z, 1);
		vec3 n1 = vec3(*it2);
		it++;
		it2++;
		i++;
		vec4 v2 = vec4(it->x, it->y,it->z,1);
		vec3 n2 = vec3(*it2);
		it++;
		it2++;
		i++;
		vec4 v3 = vec4(it->x, it->y, it->z, 1);
		vec3 n3 = vec3(*it2);
		it++;
		it2++;
		i++;
		// Heinrich would like to ask: why the fuck arent we drawig normals here,
		// but doing this in a seperate routine?! What the fuck is this?!
#ifdef USE_WIREFRAME
		DrawSingleTriangle(v1,v2,v3);
#else
		DrawSingleTriangle(v1,affineVector(n1),v2,affineVector(n2),v3,affineVector(n3));
#endif
	}
}

void Renderer::drawVertexNormals(const vector<vec3> * vertices, const vector<vec3> * normals)//,GLfloat modelDiameter){
{
	if(flagDrawVertexNormals ==false){
		return;
	}
	unsigned int i=0;
	vector<vec3>::const_iterator vit = vertices->begin();
	vector<vec3>::const_iterator nit = normals->begin();
	while (i<(normals->size())) {
		//cout<<"getting a vn"<<endl;
		vec3 v = vec3(vit->x, vit->y, vit->z);
		vec3 n = vec3(nit->x, nit->y, nit->z);
		//cout<<"normal is "<<n<<endl;
		vit++;
		nit++;
		i++;
		drawVertexNormal(v,n,m_normalLength);
	}
}

void Renderer::TransformToViewport(vec2 & v){
	v.x = (this->m_width-1)/2 + v.x*((this->m_width-1)/2);
	v.y = (this->m_height-1)/2 + v.y*((this->m_height-1)/2);
}

void Renderer::drawVertexNormal(const vec3& v,const vec3& n, GLfloat modelDiameter){

	if ((n.x==0) && (n.y==0) && (n.z==0)){
		return;
	}
	const unsigned int factor =20;

	vec3 normalizedN = normalize((vec3)(n))*(modelDiameter/factor);

	vec3 normalHead = v+normalizedN;
	vec4 normalHead4 = vec4(normalHead,1);
	vec4 v4 = vec4(v,1);

	vec2 w1 = this->TransformVertex(v);
	vec2 w2 = this->TransformVertex(normalHead4);

	this->DrawLine(w1,w2);
}


/////////////////////////////////////////////////////
//OpenGL stuff. Don't touch.

void Renderer::InitOpenGLRendering()
{
	int a = glGetError();
	a = glGetError();
	glGenTextures(1, &gScreenTex);
	a = glGetError();
	glGenVertexArrays(1, &gScreenVtc);
	GLuint buffer;
	glBindVertexArray(gScreenVtc);
	glGenBuffers(1, &buffer);
	const GLfloat vtc[]={
		-1, -1,
		1, -1,
		-1, 1,
		-1, 1,
		1, -1,
		1, 1
	};
	const GLfloat tex[]={
		0,0,
		1,0,
		0,1,
		0,1,
		1,0,
		1,1};
	glBindBuffer(GL_ARRAY_BUFFER, buffer);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vtc)+sizeof(tex), NULL, GL_STATIC_DRAW);
	glBufferSubData( GL_ARRAY_BUFFER, 0, sizeof(vtc), vtc);
	glBufferSubData( GL_ARRAY_BUFFER, sizeof(vtc), sizeof(tex), tex);

	GLuint program = InitShader( "vshader.glsl", "fshader.glsl" );
	glUseProgram( program );
	GLint  vPosition = glGetAttribLocation( program, "vPosition" );

	glEnableVertexAttribArray( vPosition );
	glVertexAttribPointer( vPosition, 2, GL_FLOAT, GL_FALSE, 0,
		0 );

	GLint  vTexCoord = glGetAttribLocation( program, "vTexCoord" );
	glEnableVertexAttribArray( vTexCoord );
	glVertexAttribPointer( vTexCoord, 2, GL_FLOAT, GL_FALSE, 0,
		(GLvoid *) sizeof(vtc) );
	glUniform1i( glGetUniformLocation(program, "texture"), 0 );
	a = glGetError();
}

void Renderer::CreateOpenGLBuffer()
{
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, gScreenTex);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, m_width, m_height, 0, GL_RGB, GL_FLOAT, NULL);
	glViewport(0, 0, m_width, m_height);
}

void Renderer::SwapBuffers()
{

	int a = glGetError();
	glActiveTexture(GL_TEXTURE0);
	a = glGetError();
	glBindTexture(GL_TEXTURE_2D, gScreenTex);
	a = glGetError();
	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_width, m_height, GL_RGB, GL_FLOAT, m_outBuffer);
	glGenerateMipmap(GL_TEXTURE_2D);
	a = glGetError();

	glBindVertexArray(gScreenVtc);
	a = glGetError();
	glDrawArrays(GL_TRIANGLES, 0, 6);
	a = glGetError();
	glutSwapBuffers();
	a = glGetError();
}

void Renderer::PlotPixel(const int & x, const int & y) {
	if ((x>=m_width) || (x<0)) return;
	if ((y>=m_height) || (y<0)) return;
	int idx1, idx2, idx3;
	idx1 = INDEX(m_width,x,y,0);
	idx2 = INDEX(m_width,x,y,1);
	idx3 = INDEX(m_width,x,y,2);
	this->m_outBuffer[idx1]=m_currentPen->getRed();
	this->m_outBuffer[idx2]=m_currentPen->getGreen();
	this->m_outBuffer[idx3]=m_currentPen->getBlue();
}


// yet another version of pixel plotting function...
void Renderer::PlotPixel(const int & x, const int & y, GLfloat avgZVal, const vec3 & pixelColor){
	if ((x>=m_width) || (x<0)) return;
	if ((y>=m_height) || (y<0)) return;
	int idx1, idx2, idx3;
	idx1 = INDEX(m_width,x,y,0);
	idx2 = INDEX(m_width,x,y,1);
	idx3 = INDEX(m_width,x,y,2);
	int zIdx = Z_INDEX(m_width,x,y);
	GLfloat depth = m_zbuffer->getFrom(zIdx);
	if (depth>avgZVal) {
		m_zbuffer->putAt(zIdx,avgZVal);
		m_outBuffer[idx1] = pixelColor.x;
		m_outBuffer[idx2] = pixelColor.y;
		m_outBuffer[idx3] = pixelColor.z;
	}
}

void Renderer::DrawLine(const vec2 & v1,const vec2 & v2) {
	if (v1.x>v2.x) {
		this->DrawLine(v2,v1);
		return;
	}
	int x = floor(v1.x+0.5);
	int y = floor(v1.y+0.5);
	int yGoal = floor(v2.y+0.5);
	int xGoal = floor(v2.x+0.5);
	int dx = xGoal - x;
	int dy = yGoal - y;
	int yStep, xStep=1;
	int primaryStep, secondaryStep;
	int * primaryVar;
	int * secondaryVar;
	if (dy<0) {
		dy*=-1;
		yStep = -1;
	}
	else {
		yStep = 1;
	}
	if (dy>dx) {
		SWAP(dy,dx);
		primaryVar = &y;
		primaryStep = yStep;
		secondaryVar = &x;
		secondaryStep = xStep;
	}
	else {
		primaryVar = &x;
		primaryStep = xStep;
		secondaryVar = &y;
		secondaryStep = yStep;
	}
	int steps = dx+1;
	int d = 2*dy-dx;
	int de = 2*dy;
	int dne = 2*(dy-dx);
	while (steps>0) {
		PlotPixel(x,y);
		if (d<0) {
			d+=de;
		}
		else {
			d+=dne;
			*secondaryVar=(*secondaryVar)+secondaryStep;
		}
		*primaryVar = (*primaryVar)+primaryStep;
		steps--;
	}
}

void Renderer::SetProjection(const mat4 & projMat) {
	this->m_projectionMatrix = projMat;
}

void Renderer::SetObjectMatrices(const mat4 & oTransform, const mat3 & nTransform) {
	this->m_objectMatrix = oTransform;
	this->m_normalMatrix = nTransform;
}

void Renderer::drawBoundingBox(vec3 boundingBoxMin, vec3 boundingBoxMax){
	if(flagDrawBoundingBox == false){
		return;
	}

	Ark ark = Ark(this);
	ark.build(boundingBoxMin,boundingBoxMax);

	//close,far,top,bottom,left,right
	vec2 BLC = this->TransformVertex(ark.vBLC);
	vec2 BLF = this->TransformVertex(ark.vBLF);
	vec2 BRC = this->TransformVertex(ark.vBRC);
	vec2 BRF = this->TransformVertex(ark.vBRF);
	vec2 TLC = this->TransformVertex(ark.vTLC);
	vec2 TLF = this->TransformVertex(ark.vTLF);
	vec2 TRC = this->TransformVertex(ark.vTRC);
	vec2 TRF = this->TransformVertex(ark.vTRF);
	
	this->DrawLine(BLC,BLF);	
	this->DrawLine(BLC,BRC);
	this->DrawLine(BLC,TLC);
	this->DrawLine(TRF,BRF);
	this->DrawLine(TRF,TLF);
	this->DrawLine(TRF,TRC);
	this->DrawLine(TLF,TLC);
	this->DrawLine(TLF,BLF);
	this->DrawLine(BLF,BRF);
	this->DrawLine(BRC,BRF);
	this->DrawLine(TRC,BRC);
	this->DrawLine(TRC,TLC);
}

//this is the scan conversion algorithm for filling polygons.
/*
void Renderer::scanAndFill(const vec4& v1, const vec4& v2, const vec4& v3,
							const vec2& w1, const vec2& w2, const vec2& w3,
							const vec3& c1, const vec3& c2, const vec3& c3) {
*/

static GLfloat getTriangleArea(const vec2& w1, const vec2& w2, const vec2& w3) {
	return 0.5 * abs (w1.x*(w2.y - w3.y)+w2.x*(w3.y-w1.y)+w3.x*(w1.y-w2.y));
}
void Renderer::scanAndFill(const VertexData& v1, const VertexData& v2, const VertexData& v3){
	GLfloat totalArea = getTriangleArea(v1.m_2dCoords,v2.m_2dCoords,v3.m_2dCoords);

	for (int i=m_scanStart; i<m_scanEnd; i++) {
		assert(i>=0);
		int lBound = m_leftBound->getFrom(i-m_scanStart);
		int rBound = m_rightBound->getFrom(i-m_scanStart);
		//if (lBound == -1) continue;
		ASSERT(lBound>=0);
		ASSERT(rBound>=0);
		//now with those bounds all we have to do is get the FUCKING
		// line and color it!
		for (int j=lBound+1; j<rBound; j++) {

			//right now we use flat shading...
			//we calculate the Z value using barycentric coordinates.

			vec2 center = vec2(j,i);
			//GLfloat a1 = length(cross(center - t2,t3-t2));
			GLfloat a1 = getTriangleArea(center,v2.m_2dCoords,v3.m_2dCoords);
			//GLfloat a2 = length(cross(center - t3,t1-t3));
			GLfloat a2 = getTriangleArea(center,v1.m_2dCoords,v3.m_2dCoords);
			GLfloat a3 = totalArea - a1 -a2;
			ASSERT(a1<=totalArea);
			ASSERT(a2<=totalArea);
			ASSERT(a3<=totalArea);
			//GLfloat avgZVal = (v1.m_zValueInNCS * a1 + v2.m_zValueInNCS*a2 + v3.m_zValueInNCS*a3)/totalArea;
			GLfloat ratio1 = a1/totalArea;
			GLfloat ratio2 = a2/totalArea;
			GLfloat avgZVal;
			vec3 avgColor;
			this->calculateColorAndZVal(v1,v2,v3,ratio1,ratio2,avgColor,avgZVal);
			PlotPixel(j,i,avgZVal,avgColor);
		}
	}
}

void Renderer::printPixelData(int x, int y) const {
	int idx_red  = INDEX(m_width,x,y,0);
	int idx_green = INDEX(m_width,x,y,1);
	int idx_blue = INDEX(m_width,x,y,2);
	GLfloat red = m_outBuffer[idx_red];
	GLfloat green = m_outBuffer[idx_green];
	GLfloat blue = m_outBuffer[idx_blue];
	int zIdx = Z_INDEX(m_width,x,y);
	GLfloat zVal = m_zbuffer->getFrom(zIdx);
	cout << "zBufferIDX = " << zIdx << " ";
	cout << idx_red << " " << idx_green << " " << idx_blue << endl;
	cout << "(x,y) = (" << x << "," << y << ") RGB = (" << red << "," << green << "," << blue << "); z = " << zVal << endl;
}

void Renderer::calculateColorAndZVal(const VertexData& v1, const VertexData& v2,
									GLfloat ratio, vec3 & colorRes, GLfloat & zValRes){

	if (m_shadingMode!=SHAD_NONE) zValRes = interpolate(v1.m_zValueInNCS,v2.m_zValueInNCS,ratio);
	switch (m_shadingMode) {
	case SHAD_NONE:
		// we are in a wireframe shader
		colorRes = vec3(m_activePen.getRed(),m_activePen.getGreen(),m_activePen.getBlue());
		return;
	case SHAD_FLAT:
		colorRes = m_flatPolygonColor;
		break;

	case SHAD_GUE:
		colorRes = interpolate(v1.combinedColor(),v2.combinedColor(),ratio);
		break;
	case SHAD_PHONG:
		// now in phong shader we should apply the interpolation over normals...
		// interpolate the normals first...
		vec3 interpolatedNormal = interpolate(vec4tovec3(v1.m_normalInCamFrame),vec4tovec3(v2.m_normalInCamFrame),ratio);
		if (length(interpolatedNormal)!=0) interpolatedNormal = normalize(interpolatedNormal);

		// well fuck me sideways... inerpolate viewers eye direction based in the two vectors.
		//vec3 viewersEyeDirection1 = normalize(vec4tovec3(-v1.m_3dCamFrameCoords));
		//vec3 viewersEyeDirection2 = normalize(vec4tovec3(-v2.m_3dCamFrameCoords));
		
		vec3 interpolatedViewersEyeDirection = normalize(interpolate(vec4tovec3(-v1.m_3dCamFrameCoords),vec4tovec3(-v2.m_3dCamFrameCoords),ratio));

		// now using the interpolated vectors, calculate the light equation you maniac!

		vec3 ambientAcc(0,0,0);
		vec3 diffuseAcc(0,0,0);
		vec3 specularAcc(0,0,0);

		for (vector<LightSource*>::const_iterator it = m_lights.begin(); it!= m_lights.end(); ++it) {
			vec3 lightHitDir1 = normalize((*it)->getHitDirection(v1.m_3dCamFrameCoords));
			vec3 lightHitDir2 = normalize((*it)->getHitDirection(v2.m_3dCamFrameCoords));
			vec3 interpolatedLightHitDir = normalize(interpolate(lightHitDir1,lightHitDir2,ratio));
			ambientAcc+=(*it)->getAmbient();
			GLfloat dotProd = dot(interpolatedNormal,interpolatedLightHitDir);
			if (dotProd<0) dotProd = 0;
			ASSERT(dotProd<=1);
			diffuseAcc = diffuseAcc + (*it)->getDiffuse()*m_currentMaterial->getDiffuse()*dotProd;

			// OK... now there goes the dam'n specular light!

			vec3 reflectionDirection = normalize(2*dot(interpolatedLightHitDir,interpolatedNormal)*interpolatedNormal - interpolatedLightHitDir);
			dotProd = dot(reflectionDirection,interpolatedViewersEyeDirection);
			ASSERT(dotProd<=1);
			if (dotProd<0) dotProd = 0;
			vec3 specTerm = (*it)->getSpecular()*m_currentMaterial->getSpecular()*pow(dotProd,m_currentMaterial->getSpecularity());
			specularAcc += specTerm;
			colorRes = ambientAcc + diffuseAcc + specularAcc;
		}
	}
}




void Renderer::calculateColorAndZVal(const VertexData& v1, const VertexData& v2, const VertexData& v3,
									GLfloat ratio1, GLfloat ratio2, vec3 & colorRes, GLfloat & zValRes){

	ASSERT(m_shadingMode!=SHAD_NONE);
	zValRes = interpolate(v1.m_zValueInNCS,v2.m_zValueInNCS,v3.m_zValueInNCS,ratio1,ratio2);
	switch(m_shadingMode) {
	case SHAD_FLAT:
		colorRes = this->m_flatPolygonColor;
		break;
	case SHAD_GUE:
		colorRes = interpolate(v1.combinedColor(),v2.combinedColor(),v3.combinedColor(),ratio1,ratio2);
		break;
	case SHAD_PHONG:
		
		// now in phong shader we should apply the interpolation over normals...
		// interpolate the normals first...
		
		vec3 interpolatedNormal = interpolate(vec4tovec3(v1.m_normalInCamFrame),vec4tovec3(v2.m_normalInCamFrame),
										vec4tovec3(v3.m_normalInCamFrame),ratio1,ratio2);

		if (length(interpolatedNormal)!=0) interpolatedNormal = normalize(interpolatedNormal);

		// well fuck me sideways... inerpolate viewers eye direction based in the two vectors.
		//vec3 viewersEyeDirection1 = normalize(vec4tovec3(-v1.m_3dCamFrameCoords));
		//vec3 viewersEyeDirection2 = normalize(vec4tovec3(-v2.m_3dCamFrameCoords));
		
		vec3 interpolatedViewersEyeDirection = 
			normalize(interpolate(	vec4tovec3(-v1.m_3dCamFrameCoords),vec4tovec3(-v2.m_3dCamFrameCoords),
									vec4tovec3(-v2.m_3dCamFrameCoords),ratio1,ratio2));

		// now using the interpolated vectors, calculate the light equation you maniac!

		vec3 ambientAcc(0,0,0);
		vec3 diffuseAcc(0,0,0);
		vec3 specularAcc(0,0,0);

		for (vector<LightSource*>::const_iterator it = m_lights.begin(); it!= m_lights.end(); ++it) {

			vec3 lightHitDir1 = normalize((*it)->getHitDirection(v1.m_3dCamFrameCoords));
			vec3 lightHitDir2 = normalize((*it)->getHitDirection(v2.m_3dCamFrameCoords));
			vec3 lightHitDir3 = normalize((*it)->getHitDirection(v3.m_3dCamFrameCoords));

			vec3 interpolatedLightHitDir = normalize(interpolate(lightHitDir1,lightHitDir2,lightHitDir3,ratio1,ratio2));
			ambientAcc+=(*it)->getAmbient();
			GLfloat dotProd = dot(interpolatedNormal,interpolatedLightHitDir);
			if (dotProd<0) dotProd = 0;
			ASSERT(dotProd<=1);
			diffuseAcc = diffuseAcc + (*it)->getDiffuse()*m_currentMaterial->getDiffuse()*dotProd;

			// OK... now there goes the dam'n specular light!

			vec3 reflectionDirection = normalize(2*dot(interpolatedLightHitDir,interpolatedNormal)*interpolatedNormal - interpolatedLightHitDir);
			dotProd = dot(reflectionDirection,interpolatedViewersEyeDirection);
			ASSERT(dotProd<=1);
			if (dotProd<0) dotProd = 0;
			vec3 specTerm = (*it)->getSpecular()*m_currentMaterial->getSpecular()*pow(dotProd,m_currentMaterial->getSpecularity());
			specularAcc += specTerm;
			colorRes = ambientAcc + diffuseAcc + specularAcc;
		}
		break;
	}
}

/*
	for (vector<LightSource*>::const_iterator it = m_lights.begin(); it!=m_lights.end(); it++) {
		vec3 lightHitDirection = normalize((*it)->getHitDirection(vertexInCamFrame));
		ambientAcc += (*it)->getAmbient();

		// now as we know the hit direction we can calculate the color... right?
		GLfloat dotProd = dot(normalInCamFrame,lightHitDirection);
		if (dotProd<0) dotProd = 0;
		ASSERT(dotProd<=1);
		diffuseAcc = diffuseAcc + (*it)->getDiffuse()*m_currentMaterial->getDiffuse()*dotProd;
		reflectionDirection = 2*dot(lightHitDirection,normalInCamFrame)*normalInCamFrame-lightHitDirection;

		// now do the same trick for the specular light...
		dotProd = dot(reflectionDirection,viewersEyeDirection);
		ASSERT(dotProd<=1);
		if (dotProd<0) dotProd = 0;
		vec3 specTerm = (*it)->getSpecular()*m_currentMaterial->getSpecular()*pow(dotProd,m_currentMaterial->getSpecularity());
		specularAcc = specularAcc + specTerm;
	}	
	*/